package com.list;

import com.tgy.util.AbstractList;

/**
 * @Author: tgy
 * @Date: 2020-09-02 09:11
 */
public class OptimizationArrayList<E> extends AbstractList<E> {


    private Object[] elementDatas = null;

    private int headIndex;


    private static int INIT_CAPACITY=10;

    public OptimizationArrayList() {

        this(INIT_CAPACITY);
    }

    public OptimizationArrayList(int capacity) {

        elementDatas = new Object[capacity > INIT_CAPACITY ? capacity : INIT_CAPACITY];
        size = 0;
        headIndex = -1;
    }



    @Override
    public E get(int index) {

        checkIndex(index);

        return (E) elementDatas[realElementIndex(index)];
    }

    @Override
    public E set(int index, E element) {

        checkIndex(index);

        int realIndex = realElementIndex(index);
        Object oldElement = elementDatas[realIndex];

        elementDatas[realIndex] = element;

        return (E) oldElement;
    }

    @Override
    public void add(int index, E element) {

        checkAddIndex(index);


        ensureCapacity(elementDatas.length - 1);

        if (size == 0){

            elementDatas[index] = element;
            headIndex = 0;

        } else {

            if (index >= size >> 1){

                for (int i = size - 1; i >= index ; i--) {

                    elementDatas[realElementIndex(i + 1)] = elementDatas[realElementIndex(i)];
                }
                elementDatas[realElementIndex(index)] = element;

            }else {

                // 往头部移动
                for (int i = 0; i <= index; i++) {

                    elementDatas[realElementIndex(i - 1)] = elementDatas[realElementIndex(i)];

                }
                headIndex = realElementIndex(-1);
                elementDatas[realElementIndex(index)] = element;

            }
        }

        size++;
    }

    @Override
    public E remove(int index) {

        checkIndex(index);

        Object removeElement = elementDatas[realElementIndex(index)];

        if (index >= (size >> 1)){

            for (int i = index; i < size ; i++) {

                elementDatas[realElementIndex(i)] = elementDatas[realElementIndex(i + 1)];
            }

            elementDatas[realElementIndex(size - 1)] = null;
        } else {

            for (int i = index; i > 0 ; i--) {

                elementDatas[realElementIndex(i)] = elementDatas[realElementIndex(i - 1)];
            }

            elementDatas[realElementIndex(0)] = null;
            headIndex = realElementIndex(1);
        }

        size--;

        shrinkCapacity();
        return (E)removeElement;
    }

    @Override
    public int indexof(E element) {

        if (element == null){

            for (int i = 0; i < size; i++) {

                if (element == elementDatas[realElementIndex(i)]){

                    return i;
                }
            }

        }else {

            for (int i = 0; i < size; i++) {

                if (element.equals(elementDatas[realElementIndex(i)])){

                    return i;
                }
            }
        }

        return -1;
    }

    @Override
    public void clear() {


        for (int i = 0; i < size; i++) {

            elementDatas[realElementIndex(i)] = null;
        }

        headIndex = -1;
        size = 0;
        elementDatas = new Object[INIT_CAPACITY];

    }

    @Override
    public String toString() {

        StringBuilder sb = new StringBuilder("size=");

        sb.append(size).append(" [ ");

        for (int i = 0; i < size; i++) {

            if (i != 0) {

                sb.append(", ");
            }

            sb.append(elementDatas[realElementIndex(i)]);
        }
         sb.append("]");
        return sb.toString();
    }


    private void ensureCapacity(int capacity){


        int oldCacapcity = elementDatas.length;


        if (size < capacity){
            return;
        }


        int newCapacity = oldCacapcity + (oldCacapcity >> 1);

        Object[] newElementDatas = new Object[newCapacity];

        for (int i = 0; i < size; i++) {

            newElementDatas[i] = elementDatas[realElementIndex(i)];
        }

        elementDatas = newElementDatas;
        headIndex = 0;

        System.out.println("容量扩到 " + newCapacity);
    }

    private void shrinkCapacity() {


        int oldCacapcity = elementDatas.length;

        int newCapacity = oldCacapcity >> 1;
        if (size > newCapacity || newCapacity < INIT_CAPACITY) {

            return;
        }

        Object[] newElementDatas = new Object[newCapacity];

        for (int i = 0; i < size; i++) {

            newElementDatas[i] = elementDatas[realElementIndex(i)];
        }

        elementDatas = newElementDatas;
        headIndex = 0;

        System.out.println("缩容到 " + newCapacity );
    }


    private int realElementIndex(int index){

        int tmpIndex = headIndex + index;

        int capacity = elementDatas.length;

        if (tmpIndex >= capacity){

            return (tmpIndex) % capacity;
        }

        if (tmpIndex < 0) {

            return (capacity + tmpIndex) % capacity;
        }

        return tmpIndex;
    }


}
